#include <cstdlib>
#include <iostream>
#include <fstream>
#include <sys/time.h>

#include "../LGraph/LGraph.h"
#include "../LGraph/Profile.h"

#define GRAPH_FILE "/wrk/stacc-sna/volodymyr/november/binary/lgraph_strong_symmetrical_2011_11.bin"
#define PROFILE_FILE "/wrk/stacc-sna/volodymyr/november/binary/profiles_2011_11.bin"
#define JAN2003 1041382800

using namespace std;

typedef short distance_type;


bool hasService(User_profile *p)
{
    return
        p->min_srv1_month_dt > JAN2003 ||

        p->min_srv2_month_dt > JAN2003 ||

        p->min_srv4_order_dt > JAN2003 ||

        p->min_srv5_order_dt > JAN2003 ||

        p->min_srv6_order_dt > JAN2003 ||

        p->min_srv7_order_dt > JAN2003 ||

        p->min_srv8_dt       > JAN2003;
}


// Breadth first search used to count all the elements in the connected components starting from the
// the vertex v1, it is received in the parameters of the method.
int BFS(LGraph *graph, Users_data *profile, vertex_id_type v1, distance_type level[], ofstream& ff)
{
    //Check the v1 as visited
    level[v1] = 0;
    std::queue<vertex_id_type> current;
    int cont = 0; // count the nodes which are taken from the queue and therefore in the connected component


    // put the v1 in a queue to start the traverse from this node
    current.push(v1);

    // while there are still nodes to check
    while (!current.empty())
    {
        vertex_id_type c = current.front();
        ff << c << " ";
        current.pop();

        if (profile->user_profiles[c].country_code != 246)
            continue;

        cont++;

        for (LGraph::iterator e = graph->iterate_adjacent_edges(c); !e.end(); e++)
        {
            // if the node adjacent to the vertex took from the queue has been already visited, continue
            if (level[(*e).v2] >= 0)
                continue;
            else if(hasService(&profile->user_profiles[(*e).v2]))
            {
                // else if it has not been visited yet, then add to the queue and check it.
                current.push((*e).v2);
                level[(*e).v2] = level[c] + 1;
            }
        }
    }
    ff << endl;
    return cont;
}

timespec timer_start()
{
    timespec ts;
    ts.tv_sec = 0;
    ts.tv_nsec = 0;
    clock_settime(CLOCK_PROCESS_CPUTIME_ID, &ts);

    return ts;
}

void timer_stop(timespec *ts)
{
    clock_gettime(CLOCK_PROCESS_CPUTIME_ID, ts);
}


int main()
{

    timespec ts = timer_start();
    // Load the graph from the file specified in the first parameter
    LGraph *graph = LGraph::read_from_file(GRAPH_FILE);
    vertex_id_type nvertices = graph->vertex_count;
    timer_stop(&ts);
    //cout << "LGraph loaded in: " << ts.tv_sec << endl;


    ts = timer_start();
    Users_data *profile = Users_data::read_from_binary_file(PROFILE_FILE);
    timer_stop(&ts);
    //cout << "Profile loaded in: " << ts.tv_sec << endl;


    ofstream ff("ids");
    //ofstream ff2("edgelist");
    long str_size = nvertices * sizeof(distance_type);

    //Array used to check the visited nodes
    distance_type *baseline = (distance_type *) malloc( str_size);

    memset(baseline, -1, (str_size));

    ts = timer_start();
    unsigned int cc = 0;
    for (unsigned i = 0; i < nvertices; i++)
    {
        // if the node i has not been visited yet, then execute the BFS from i
        if (baseline[i] == -1 && hasService(&profile->user_profiles[i]) &&
                profile->user_profiles[i].country_code == 246)
        {
            cc++;
            int size = BFS(graph, profile, i , baseline, ff);
            cout << cc << " " << size << endl;
        }
    }
    timer_stop(&ts);
    //cout << "Calculations took: " << ts.tv_sec << endl;

    ff.close();
    //ff2.close();
    return 0;
}

